// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
import 'dart:io';

export 'package:image_picker_platform_interface/image_picker_platform_interface.dart'
    show
    kTypeImage,
    kTypeVideo,
    ImageSource,
    CameraDevice,
    LostData,
    LostDataResponse,
    PickedFile,
    XFile,
    RetrieveType;

/// Provides an easy way to pick an image/video from the image library,
/// or to take a picture/video with the camera.
class ImagePicker {
  /// The platform interface that drives this plugin
  @visibleForTesting
  static ImagePickerPlatform get platform => ImagePickerPlatform.instance;

  /// Returns a [PickedFile] object wrapping the image that was picked.
  ///
  /// The returned [PickedFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions.
  ///
  /// The `source` argument controls where the image comes from. This can
  /// be either [ImageSource.camera] or [ImageSource.gallery].
  ///
  /// Where iOS supports HEIC images, Android 8 and below doesn't. Android 9 and above only support HEIC images if used
  /// in addition to a size modification, of which the usage is explained below.
  ///
  /// If specified, the image will be at most `maxWidth` wide and
  /// `maxHeight` tall. Otherwise the image will be returned at it's
  /// original width and height.
  /// The `imageQuality` argument modifies the quality of the image, ranging from 0-100
  /// where 100 is the original/max quality. If `imageQuality` is null, the image with
  /// the original quality will be returned. Compression is only supported for certain
  /// image types such as JPEG and on Android PNG and WebP, too. If compression is not supported for the image that is picked,
  /// a warning message will be logged.
  ///
  /// Use `preferredCameraDevice` to specify the camera to use when the `source` is [ImageSource.camera].
  /// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device.
  /// Defaults to [CameraDevice.rear]. Note that Android has no documented parameter for an intent to specify if
  /// the front or rear camera should be opened, this function is not guaranteed
  /// to work on an Android device.
  ///
  /// In Android, the MainActivity can be destroyed for various reasons. If that happens, the result will be lost
  /// in this call. You can then call [getLostData] when your app relaunches to retrieve the lost data.
  ///
  /// See also [getMultiImage] to allow users to select multiple images at once.
  ///
  /// The method could throw [PlatformException] if the app does not have permission to access
  /// the camera or photos gallery, no camera is available, plugin is already in use,
  /// temporary file could not be created (iOS only), plugin activity could not
  /// be allocated (Android only) or due to an unknown error.
  @Deprecated('Switch to using pickImage instead')
  Future<PickedFile?> getImage({
    required ImageSource source,
    double? maxWidth,
    double? maxHeight,
    int? imageQuality,
    CameraDevice preferredCameraDevice = CameraDevice.rear,
  }) {
    return platform.pickImage(
      source: source,
      maxWidth: maxWidth,
      maxHeight: maxHeight,
      imageQuality: imageQuality,
      preferredCameraDevice: preferredCameraDevice,
    );
  }

  /// Returns a [List<PickedFile>] object wrapping the images that were picked.
  ///
  /// The returned [List<PickedFile>] is intended to be used within a single APP session. Do not save the file path and use it across sessions.
  ///
  /// Where iOS supports HEIC images, Android 8 and below doesn't. Android 9 and above only support HEIC images if used
  /// in addition to a size modification, of which the usage is explained below.
  ///
  /// This method is not supported in iOS versions lower than 14.
  ///
  /// If specified, the images will be at most `maxWidth` wide and
  /// `maxHeight` tall. Otherwise the images will be returned at it's
  /// original width and height.
  /// The `imageQuality` argument modifies the quality of the images, ranging from 0-100
  /// where 100 is the original/max quality. If `imageQuality` is null, the images with
  /// the original quality will be returned. Compression is only supported for certain
  /// image types such as JPEG and on Android PNG and WebP, too. If compression is not supported for the image that is picked,
  /// a warning message will be logged.
  ///
  /// The method could throw [PlatformException] if the app does not have permission to access
  /// the camera or photos gallery, no camera is available, plugin is already in use,
  /// temporary file could not be created (iOS only), plugin activity could not
  /// be allocated (Android only) or due to an unknown error.
  ///
  /// See also [getImage] to allow users to only pick a single image.
  @Deprecated('Switch to using pickMultiImage instead')
  Future<List<PickedFile>?> getMultiImage({
    double? maxWidth,
    double? maxHeight,
    int? imageQuality,
  }) {
    return platform.pickMultiImage(
      maxWidth: maxWidth,
      maxHeight: maxHeight,
      imageQuality: imageQuality,
    );
  }

  /// Returns a [PickedFile] object wrapping the video that was picked.
  ///
  /// The returned [PickedFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions.
  ///
  /// The [source] argument controls where the video comes from. This can
  /// be either [ImageSource.camera] or [ImageSource.gallery].
  ///
  /// The [maxDuration] argument specifies the maximum duration of the captured video. If no [maxDuration] is specified,
  /// the maximum duration will be infinite.
  ///
  /// Use `preferredCameraDevice` to specify the camera to use when the `source` is [ImageSource.camera].
  /// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device.
  /// Defaults to [CameraDevice.rear].
  ///
  /// In Android, the MainActivity can be destroyed for various fo reasons. If that happens, the result will be lost
  /// in this call. You can then call [getLostData] when your app relaunches to retrieve the lost data.
  ///
  /// The method could throw [PlatformException] if the app does not have permission to access
  /// the camera or photos gallery, no camera is available, plugin is already in use,
  /// temporary file could not be created and video could not be cached (iOS only),
  /// plugin activity could not be allocated (Android only) or due to an unknown error.
  ///
  @Deprecated('Switch to using pickVideo instead')
  Future<PickedFile?> getVideo({
    required ImageSource source,
    CameraDevice preferredCameraDevice = CameraDevice.rear,
    Duration? maxDuration,
  }) {
    return platform.pickVideo(
      source: source,
      preferredCameraDevice: preferredCameraDevice,
      maxDuration: maxDuration,
    );
  }

  /// Retrieve the lost [PickedFile] when [selectImage] or [selectVideo] failed because the  MainActivity is destroyed. (Android only)
  ///
  /// Image or video can be lost if the MainActivity is destroyed. And there is no guarantee that the MainActivity is always alive.
  /// Call this method to retrieve the lost data and process the data according to your APP's business logic.
  ///
  /// Returns a [LostData] object if successfully retrieved the lost data. The [LostData] object can represent either a
  /// successful image/video selection, or a failure.
  ///
  /// Calling this on a non-Android platform will throw [UnimplementedError] exception.
  ///
  /// See also:
  /// * [LostData], for what's included in the response.
  /// * [Android Activity Lifecycle](https://developer.android.com/reference/android/app/Activity.html), for more information on MainActivity destruction.
  @Deprecated('Switch to using retrieveLostData instead')
  Future<LostData> getLostData() {
    return platform.retrieveLostData();
  }

  /// Returns an [XFile] object wrapping the image that was picked.
  ///
  /// The returned [XFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions.
  ///
  /// The `source` argument controls where the image comes from. This can
  /// be either [ImageSource.camera] or [ImageSource.gallery].
  ///
  /// Where iOS supports HEIC images, Android 8 and below doesn't. Android 9 and above only support HEIC images if used
  /// in addition to a size modification, of which the usage is explained below.
  ///
  /// If specified, the image will be at most `maxWidth` wide and
  /// `maxHeight` tall. Otherwise the image will be returned at it's
  /// original width and height.
  /// The `imageQuality` argument modifies the quality of the image, ranging from 0-100
  /// where 100 is the original/max quality. If `imageQuality` is null, the image with
  /// the original quality will be returned. Compression is only supported for certain
  /// image types such as JPEG and on Android PNG and WebP, too. If compression is not supported for the image that is picked,
  /// a warning message will be logged.
  ///
  /// Use `preferredCameraDevice` to specify the camera to use when the `source` is [ImageSource.camera].
  /// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device.
  /// Defaults to [CameraDevice.rear]. Note that Android has no documented parameter for an intent to specify if
  /// the front or rear camera should be opened, this function is not guaranteed
  /// to work on an Android device.
  ///
  /// In Android, the MainActivity can be destroyed for various reasons. If that happens, the result will be lost
  /// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data.
  ///
  /// See also [pickMultiImage] to allow users to select multiple images at once.
  ///
  /// The method could throw [PlatformException] if the app does not have permission to access
  /// the camera or photos gallery, no camera is available, plugin is already in use,
  /// temporary file could not be created (iOS only), plugin activity could not
  /// be allocated (Android only) or due to an unknown error.
  Future<XFile?> pickImage({
    required ImageSource source,
    double? maxWidth,
    double? maxHeight,
    int? imageQuality,
    CameraDevice preferredCameraDevice = CameraDevice.rear,
  }) {
    return platform.getImage(
      source: source,
      maxWidth: maxWidth,
      maxHeight: maxHeight,
      imageQuality: imageQuality,
      preferredCameraDevice: preferredCameraDevice,
    );
  }

  /// Returns a [List<XFile>] object wrapping the images that were picked.
  ///
  /// The returned [List<XFile>] is intended to be used within a single APP session. Do not save the file path and use it across sessions.
  ///
  /// Where iOS supports HEIC images, Android 8 and below doesn't. Android 9 and above only support HEIC images if used
  /// in addition to a size modification, of which the usage is explained below.
  ///
  /// This method is not supported in iOS versions lower than 14.
  ///
  /// If specified, the images will be at most `maxWidth` wide and
  /// `maxHeight` tall. Otherwise the images will be returned at it's
  /// original width and height.
  /// The `imageQuality` argument modifies the quality of the images, ranging from 0-100
  /// where 100 is the original/max quality. If `imageQuality` is null, the images with
  /// the original quality will be returned. Compression is only supported for certain
  /// image types such as JPEG and on Android PNG and WebP, too. If compression is not supported for the image that is picked,
  /// a warning message will be logged.
  ///
  /// The method could throw [PlatformException] if the app does not have permission to access
  /// the camera or photos gallery, no camera is available, plugin is already in use,
  /// temporary file could not be created (iOS only), plugin activity could not
  /// be allocated (Android only) or due to an unknown error.
  ///
  /// See also [pickImage] to allow users to only pick a single image.
  Future<List<XFile>?> pickMultiImage({
    double? maxWidth,
    double? maxHeight,
    int? imageQuality,
  }) {
    return platform.getMultiImage(
      maxWidth: maxWidth,
      maxHeight: maxHeight,
      imageQuality: imageQuality,
    );
  }

  /// Returns an [XFile] object wrapping the video that was picked.
  ///
  /// The returned [XFile] is intended to be used within a single APP session. Do not save the file path and use it across sessions.
  ///
  /// The [source] argument controls where the video comes from. This can
  /// be either [ImageSource.camera] or [ImageSource.gallery].
  ///
  /// The [maxDuration] argument specifies the maximum duration of the captured video. If no [maxDuration] is specified,
  /// the maximum duration will be infinite.
  ///
  /// Use `preferredCameraDevice` to specify the camera to use when the `source` is [ImageSource.camera].
  /// The `preferredCameraDevice` is ignored when `source` is [ImageSource.gallery]. It is also ignored if the chosen camera is not supported on the device.
  /// Defaults to [CameraDevice.rear].
  ///
  /// In Android, the MainActivity can be destroyed for various fo reasons. If that happens, the result will be lost
  /// in this call. You can then call [retrieveLostData] when your app relaunches to retrieve the lost data.
  ///
  /// The method could throw [PlatformException] if the app does not have permission to access
  /// the camera or photos gallery, no camera is available, plugin is already in use,
  /// temporary file could not be created and video could not be cached (iOS only),
  /// plugin activity could not be allocated (Android only) or due to an unknown error.
  ///
  Future<XFile?> pickVideo({
    required ImageSource source,
    CameraDevice preferredCameraDevice = CameraDevice.rear,
    Duration? maxDuration,
  }) {
    return platform.getVideo(
      source: source,
      preferredCameraDevice: preferredCameraDevice,
      maxDuration: maxDuration,
    );
  }

  /// Retrieve the lost [XFile] when [pickImage], [pickMultiImage] or [pickVideo] failed because the MainActivity
  /// is destroyed. (Android only)
  ///
  /// Image or video can be lost if the MainActivity is destroyed. And there is no guarantee that the MainActivity is always alive.
  /// Call this method to retrieve the lost data and process the data according to your APP's business logic.
  ///
  /// Returns a [LostDataResponse] object if successfully retrieved the lost data. The [LostDataResponse] object can \
  /// represent either a successful image/video selection, or a failure.
  ///
  /// Calling this on a non-Android platform will throw [UnimplementedError] exception.
  ///
  /// See also:
  /// * [LostDataResponse], for what's included in the response.
  /// * [Android Activity Lifecycle](https://developer.android.com/reference/android/app/Activity.html), for more information on MainActivity destruction.
  Future<LostDataResponse> retrieveLostData() {
    return platform.getLostData();
  }

  ///
  /// 只支持iOS
  static const customPlatform = MethodChannel("plugins.flutter.io/image_picker");

  Future<XFile?> customPickImage({
    required ImageSource source,
    double? maxWidth,
    double? maxHeight,
    int? imageQuality,
    CameraDevice preferredCameraDevice = CameraDevice.rear,
    bool? imageAllowsVideo = false, /// 相册是否允许选择视频
    bool? imageAllowsEditing = false,  /// 选择图片是否可以裁剪
    bool? imageHEICtoJPG = false,  /// 图片HEIC格式转换jpg
    bool? isRotation = false       /// 是否适配旋转
  }) async {
    if(Platform.isAndroid){
      return pickImage(
        source: source,
        maxWidth: maxWidth,
        maxHeight: maxHeight,
        imageQuality: imageQuality,
        preferredCameraDevice: preferredCameraDevice,
      );
    }

    /// 视频不支持裁剪
    if(imageAllowsEditing==true) imageAllowsVideo = false;

    var params = <String,dynamic>{
      "source": source==ImageSource.camera?0:1,///0-相机，1-相册
      "maxWidth": maxWidth,
      "maxHeight": maxHeight,
      "imageQuality": imageQuality,
      "cameraDevice": preferredCameraDevice==CameraDevice.rear?0:1,///0-后置摄像头，1-前置摄像头
      "imageAllowsVideo": imageAllowsVideo,
      "imageAllowsEditing": imageAllowsEditing,
      "imageHEICtoJPG": imageHEICtoJPG,
      "isRotation" : isRotation
    };
    final path = await customPlatform.invokeMethod('customPickImage',params);
    if(path==null) return null;

    if(path is Map){
      var tempPath = path["path"];
      if(tempPath!=null) return XFile(tempPath);
      return null;
    }

    return XFile(path);
  }

  Future<List<XFile>?> customPickMultiImage({
    double? maxWidth,
    double? maxHeight,
    int? imageQuality,
    bool? imageAllowsVideo = false,
    String? customSavePath,
    bool? imageHEICtoJPG = false,
  }) async {
    if(Platform.isAndroid){
      return pickMultiImage(
        maxWidth: maxWidth,
        maxHeight: maxHeight,
        imageQuality: imageQuality,
      );
    }

    var params = <String,dynamic>{
      "maxWidth": maxWidth,
      "maxHeight": maxHeight,
      "imageQuality": imageQuality,
      "imageAllowsVideo": imageAllowsVideo,
      "customSavePath":customSavePath,
      "imageHEICtoJPG": imageHEICtoJPG,
    };
    final List? paths = await customPlatform.invokeMethod('customPickMultiImage',params);
    if(paths==null) return null;
    List<XFile> files = [];
    for (var element in paths) {
      if(element is Map){
        var path = element["path"];
        double? createTime = element['createTime'];
        if(path!=null && createTime!=null) files.add(XFile(path,name: element["name"], lastModified: DateTime.fromMillisecondsSinceEpoch(createTime.toInt() )));
      } else {
        files.add(XFile(element));
      }
    }
    return files;
  }

}
